package com.artezio.buyndrive.dao.impl;

import com.artezio.buyndrive.dao.Dao;
import com.artezio.buyndrive.domain.Country;
import org.apache.commons.lang.ArrayUtils;
import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

@Repository
public abstract class AbstractHibernateDao<T extends Object> implements Dao<T> {

    @Autowired
    private SessionFactory sessionFactory;

    private Class<T> persistentClass;

    public Class<T> getPersistentClass() {
        return persistentClass;
    }

    @SuppressWarnings("unchecked")
    public AbstractHibernateDao() {
        Type type = getClass().getGenericSuperclass();
        while (type != null) {
            if (type instanceof ParameterizedType) {
                persistentClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
                break;
            } else if (type instanceof Class) {
                type = ((Class) type).getGenericSuperclass();
            }
        }
        //persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @SuppressWarnings("unchecked")
    public T findById(Long id/*
             * , String... properties
             */) {
        T t = (T) getSession().get(getPersistentClass(), id);
        //HibernateInitializer.initializeProperties(t, properties);
        return t;
    }

    @SuppressWarnings("unchecked")
    public List<T> findByIds(List<Long> ids, String... properties) {

        Criteria criteria = getSession().createCriteria(getPersistentClass());
        criteria.add(Restrictions.in("id", ids));
        appendJoinForPropertiesToCriteria(criteria, properties);

        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        return criteria.list();
    }

    public void saveOrUpdate(T entity) {
        getSession().saveOrUpdate(entity);
    }

    @SuppressWarnings("unchecked")
    public Long add(T entity) {
        return (Long) getSession().save(entity);
    }

    @SuppressWarnings("unchecked")
    public T merge(T entity) {
        return (T) getSession().merge(entity);
    }

    protected void appendJoinForPropertiesToCriteria(Criteria criteria, String... properties) {
        if (ArrayUtils.isEmpty(properties)) {
            return;
        }

        for (String propertyName : properties) {
            criteria.setFetchMode(propertyName, FetchMode.JOIN);
        }
    }

    @SuppressWarnings("unchecked")
    public List<T> getAll() {
        return getSession().createCriteria(getPersistentClass()).list();
    }

    public void delete(T entity) {
        getSession().delete(entity);
    }
}
